#include "tcpclient.h"
#include "dbg.h"
#include "tick.h"
#include "file_io.h"
#include "cJSON.h"
#include "serial_port.h"
#include "cfg_info.h"

#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <regex.h>
#include <sys/types.h>

int _serial = -1;
static int listenfd = 0;
static bool s_isRun = false;
struct apn_info _ApnCfg = {"CMIOT", "username", "password"};

struct infor
{
    struct sockaddr_in cliaddr;
    int connfd;
};

void app_quit(int signo)
{
    if (SIGINT == signo)
    {
        s_isRun = false;
        close(listenfd);
        dbg_msg("App is quit, bye!");
    }
}

char *pick_regex(const char *string, const char *pattern)
{
    int err;
    regex_t compiled;
    if ((err = regcomp(&compiled, pattern, REG_EXTENDED | REG_ICASE | REG_NEWLINE)) != 0)
    {
#if 0
        char errbuf[1024];
        regerror(err, &compiled, errbuf, sizeof(errbuf));
        dbg_err("error:%s",errbuf);
#endif
        return NULL;
    }

    regmatch_t pmatch[2];
    err = regexec(&compiled, string, 2, pmatch, REG_NOTBOL);
    if (err != 0)
    {
        return NULL;
    }

    if (compiled.re_nsub != 1)
        return NULL;
    if (pmatch[1].rm_so == -1)
        return NULL;

    int len = pmatch[1].rm_eo - pmatch[1].rm_so;
    char *value = (char *)malloc(len + 1);
    if (value == NULL)
        return NULL;

    memset(value, 0, len + 1);
    memcpy(value, string + pmatch[1].rm_so, len);
    regfree(&compiled);

    return value;
}

void parse_client_data(char *_buf, int len)
{
    printf("cmd type: %d ", len);
    dbg_msg("msg: %s", _buf);
    cJSON *root = cJSON_Parse(_buf);
    if (root == NULL)
    {
        return;
    }

    cJSON *_type = cJSON_GetObjectItem(root, "type");
    if (_type != NULL)
    {
        dbg_msg("Msg type: %d", _type->valueint);
    }

    /* eg: {"type": 1, "data":{"apn": "CMIOT", "user": "username", "passwd": "password"}} */
    cJSON *_data = cJSON_GetObjectItem(root, "data");
    if (_data != NULL)
    {
        cJSON *temp = cJSON_GetObjectItem(_data, "apn");
        if (NULL != temp)
        {
            dbg_msg("apn:%s", temp->valuestring);
        }

        temp = cJSON_GetObjectItem(_data, "user");
        if (NULL != temp)
        {
            dbg_msg("user:%s", temp->valuestring);
        }

        temp = cJSON_GetObjectItem(_data, "passwd");
        if (NULL != temp)
        {
            dbg_msg("passwd:%s", temp->valuestring);
        }
    }
}

void *thread_tcp_fun(void *arg)
{
    char buf[BUFSIZ];
    char str[INET_ADDRSTRLEN];
    struct infor *var = (struct infor *)arg;
    int ret;

    printf("client [%s] at port %d connected.\n",
           inet_ntop(AF_INET, &(var->cliaddr.sin_addr.s_addr), str, sizeof(str)),
           ntohs(var->cliaddr.sin_port));

    while (s_isRun)
    {
        ret = read(var->connfd, buf, BUFSIZ);
        if (ret == 0)
        {
            break;
        }
        else if (ret > 0)
        {
            // parse json data
            parse_client_data(buf, ret);
        }
    }

    printf("client [%s] at port %d disconnected.\n",
           inet_ntop(AF_INET, &(var->cliaddr.sin_addr.s_addr), str, sizeof(str)),
           ntohs(var->cliaddr.sin_port));
    close(var->connfd);
    return NULL;
}

void _serial_port_send_data(const char *buffer, int bufferLen)
{
    if (write(_serial, buffer, bufferLen) <= 0)
    {
        dbg_err("failed to write serial port!");
    }
    else
    {
        dbg_msg("serialport send: %s", buffer);
    }
}

/* AT data */
void _process_sreial_port_recv_data(char *data, int dataLen)
{
    char *reg_csq = pick_regex(data, "CSQ: ([0-9]*),");
    if (NULL != reg_csq)
    {
        int csq = atoi(reg_csq);
        dbg_msg("CSQ: %s --> %d", reg_csq, csq);
    }

    char *reg_cpin = pick_regex(data, "CPIN: ([A-Za-z ?]*)");
    if (NULL != reg_cpin)
    {
        dbg_msg("CPIN: %s", reg_cpin);
    }

    //
}

void *check_dial_state(void *arg)
{
    Tick_DT _Tick = tick_get();
    ui32 _tick = 0;
    int time_out = 1;

    dbg_msg("start dial check");
    while (s_isRun)
    {
        // 设置超时
        if (tick_timeout(_Tick, 1000))
        {
            if ((time_out - 1) == (_tick % time_out))
            {
#if 1 /* test at cmd */
                // check CSQ -- AT+CSQ?
                char *at_cmd = "+CSQ: 28,99\r\n";

                // check SIM -- AT+CPIN?
                // char *at_cmd = "+CPIN: READY\r\n";

                // set apn -- AT+CPIN?
                // char *at_cmd = "+CPIN: not insert\r\n";

                _serial_port_send_data(at_cmd, strlen(at_cmd));
#endif
            }
            _tick++;
            _Tick = tick_get();
        }
        usleep(1000);
    }

    return NULL;
}

int _serialport_readbyte(uint8_t *buffer)
{
    int len, fs_sel;
    fd_set fs_read;
    struct timeval time;

    FD_ZERO(&fs_read);
    FD_SET(_serial, &fs_read);
    time.tv_sec = 0;
    time.tv_usec = 100000;
    fs_sel = select(_serial + 1, &fs_read, NULL, NULL, &time);
    if (fs_sel > 0)
    {
        len = read(_serial, (char *)buffer, 0x01);
        if (len > 0)
            return 0;
    }

    return -1;
}

void *thread_uart_fun(void *pragme)
{
    static ui8 _buffer[10240];
    static int _bufferLen = 0;
    ui8 temp = 0;
    int ret = 0;

    while (true)
    {
        temp = 0x00;
        ret = _serialport_readbyte(&temp);
        if (ret == 0)
        {
            _buffer[_bufferLen++] = temp;
            temp = 0x00;
        }
        else if (_bufferLen > 0 && ret == -1)
        {
            _process_sreial_port_recv_data((char *)_buffer, _bufferLen);
            _bufferLen = 0;
            temp = 0x00;
        }
        usleep(100);
    }

    return NULL;
}

int main(int argc, char *argv[])
{
    int connfd;
    int i = 0;
    pthread_t tid;
    struct infor ts[128];
    memset(ts, 0x00, 128 * sizeof(struct infor));
    signal(SIGINT, app_quit);
    print_version();

    struct sockaddr_in cliaddr;
#ifdef __arm__
    listenfd = start_up("127.0.0.1", 8888);
#else
    listenfd = start_up("192.168.0.159", 8888);
#endif

    if (listenfd > 0)
    {
        s_isRun = true;
    }
    printf("===> Accepting client connections, listen port [%d] <===\n", 8888);

    _serial = serial_port_init("/dev/ttyUSB0", 9600);
    pthread_t tid_uart;
    pthread_create(&tid_uart, NULL, thread_uart_fun, NULL);
    pthread_detach(tid_uart);

    pthread_t tid_dial;
    pthread_create(&tid_dial, NULL, check_dial_state, NULL);
    pthread_detach(tid_dial);

    while (s_isRun)
    {
        socklen_t addrlen = sizeof(struct sockaddr);
        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &addrlen);
        ts[i].cliaddr = cliaddr;
        ts[i].connfd = connfd;
        pthread_create(&tid, NULL, thread_tcp_fun, (void *)&ts[i]);
        pthread_detach(tid);
        i++;
    }

    return 0;
}
